//: Playground - noun: a place where people can play

import UIKit



/*---------------------1.Swift的基本数据类型-----------------------------*/

var study = 0

if study < 100
{

    //1.1 Swift 关键字和标示符

    //这样会报错
    //var class = 2;
    var `class` = 2 //可以这样定义于标示符冲突的变量，但是不建议

    /*
    关键字分四类：声明相关的（private)语句相关的(break)类型表达式相关的(self)。特定上下文相关的(left,lazy)。
    */

    //变量的声明必须确定类型，可以自己显示的指定，也可以初始化让系统推断类型,下面都可以。

    var b:Int
    var a = 10
    var c :Int = 100

    //也可以一次声明多个变量,类型也不需要相同

    var d=20,e:Int,f="hello "

    //常量声明用的是let，其他部分声明方面和变量一样

    let constA = 10
    //constA =2 不能重新赋值


    //数据输出
    print(constA)

    print("a的值是：\(a),f的值是:\(f)")//用\(变量的)形式插入到字符串输出

    //整形

    /// 默认的Int长度等于所在平台的原生字长，细分了Int8，16，32和Uint8，Uint16，32等等

    //var uchar :UInt8 = 256 //范围是0-255 超出范围 报错了

    var unchar :UInt8 = 0128 //前面加0也可以
    var binaryInt = 0b1001_0100 //可以用二进制表示,支持下划线分开
    var hexInt = 0xAf
    var octalInt = 0o777 //这是八进制

    //浮点数类型 1.十进制，2.科学技术法  5.2e3 = 5200 3.16进制数表示 0x5.a2p4, 以0x开头，p代表4

    //的p次方 = 0x5.a2 * 2^4
    //浮点数提供了正无穷大，负无穷大和非数 三个 特殊的浮点数。

    //正的浮点数 除以0.0得到正无穷，负的浮点数除以0.0得到负无穷，0.0除以0.0的非数

    var float_a = 5.0,float_b = -5.0

    print("正无穷和正无穷相等\(float_a/0.0==(4.0/0.0))，负无穷和负无穷相等\(float_b/0.0==(-4.0/0.0))，非数和非数自己都不相等\(0.0/0.0==(0.0/0.0))")

    //非浮点数除以0.0会报错的
    //var int_num= 5
    //int_num/0.0
    //
    //
    //
    //数据类型的转换，数据类型转换如果数值超出范围就会报错，swift属于强类型语言，不同类型的值经常需要转换

    var t_a:UInt8=100
    var t_b:UInt16=10

    //类型不一样，直接编译出错
    //t_a=t_b;
    //t_b = t_a

    t_b = UInt16(t_a)//强制转换时可以的

    //Bool值只能用true和false，不能用0或非0 表示，其他类型的值也不转化为bool值。在该使用bool值的地方一定要用Bool

    //这是不行的
    //var i = 1
    //if i
    //{
    //    //
    //}

    //var i = true
    //if i
    //{
    //    //这是可以的
    //}
    //


    //元组 ：tuple 使用圆花括号把多个值复合成一个复合值,定义元组类型的变量

    var people = ("zhangsan",14,170.00) //这是系统推断的方式

    var book :(String,Float,Int)//可以事先声明
    book = ("bookname",30.0,2)
    print(book)

    var test : (Int,(Float,String))//元组成员又是元组

    test = (10,(100.0,"bookName"))

    //元组访问

    print(test.0)//可以通过点加下标的方式访问
    print(test.1.0)
    //元组的拆分

    var (num,(price,name))=test
    print("\(num),\(price),\(name)")

    let (c_num,(_,c_name))=test //可以用下划线表示要忽略元素
    //可以使用key：value的形式为每个元组内的变量指明名字

    var health = (height:175,weight:65)//可以直接写值

    var score : (swift:Int,name:String)//也可以后面直接跟这个变量类型声明

    score = (80,"real") //不写指定值只能按照格式顺序
    score=(name:"real",swift:100)//指定值可以打乱顺序.所以访问元组的数据也可以通过元组的变量名访问
    print(score.name)



    //可选类型 -

    var str_val = "123"


    var val1:Int? = Int(str_val) //.toInt这个方法找不到了,变成用构造器转换了不加?代码编译不过去，因为转换可能会失败。
    //如果没有选择？，表示职能转换为Int，在转化失败的时候 属于『值缺失』，需要处理值缺失的时候即使nil

    //Switf里面 nil和OC的不同，OC里面的nil表示一个不存在对象的指针，Swift里面的nil表示的是确定的值,表示值缺失

    //只有可选类型的变量或常量才能接受nil，非可选的变量和常量不可以接受,一个可选类型没有初始化，则值就是nil

    var option_a:String? = "1234"

    // 强制解析 ：可选类型的变量和普通类型的变量不是相同的类型。比如不能直接把Int?直接转换为Int，用！表示 已知该变量有值，请提起其中的值

    var value:Int = Int(option_a!)!//------需要这样写才能把可选类型的字符串转化为整数！！！

    var n1 :Int? = 100
    var n2 :Int? = 50
    let n3 = n1!+n2!
    print(n3)

    //强制解析必须是可选类型，并且里面确实有值才能成功，否则程序会出错，可以使用 if val!=nil 表示如果这个可选类型的变量不等于nil


    //可选绑定 ：用于判断可选类型的变量和常量是否有值，如果可选的有值就赋值给另一个临时的变量或者常量
    //可以在if分支对可选类型的变量进行判断

    var str :String!="1234"
    if var tem = str//如果str的值为nil就不会去来这里了
    {
        print(tem)
    }
    else
    {
        print("不能解析")
    }

    //隐式可选类型

    //除了在任意已有类型的后面加？表示可选类型外，还可以在后面加！表示可选类型
    //Int!和Int？区别是，当程序需要获取Int?类型的变量或常量值时候必须在变量后面添加！执行强制解析，
    //Int！不用

    var aa :Int? = 100
    var ab :Int! = 50
    var cc :Int = aa!
    var dd :Int=ab

    //类型别名

    //typealias 相当 typedef

    //typealias myInt  = Int
    // 字符和字符串

    //Character 表示单个字符 String 表示字符串，字符可以用单个字符，转义字符，"\u{n}"unicode编码表示 n表示
    //1-8位16进制数

    var chara:Character = "\u{2666}"
    print(chara)
    chara = "好"

    str = "两个不是图片的星星✨✨"
    //目前最新的swift countElement已经被改成了count
    print(str.characters.count)

    //插入字符串可以用字符串插值的方式

    str = "前面插入了这些 \(str)"

    //for char in str  //无法编译、
    //{
    //    print(char)
    //}
    //

    var str_1 = "first✨🚩"
    //var str_2 = "firsy"
    //print(str_1==str_2)
    //var hasPrefix:Bool = str_1.hasPrefix("first")//是否以特定的开头
    //var hasSuffix:Bool = str_2.hasSuffix("sy")
    //
    ////访问字符串中的UNICODE码

    print("这里是UTF8编码：能用UTF8表示的就显示一个数，不能用显示的就会输出多个，每个表示一个字节，分多个字节输出，星星用了226 156 168表示对")
    for codeUnit8 in str_1.utf8//这里返回的是一个utf8View类型的值，是一个包含多个Utf8的值的集合
    {
        print(codeUnit8,terminator:" ")//现在改成这个了 不换行输出用这个语句

    }
    print("这里是UTF16编码：和UTF8类似，这里每个表示一个字节")
    for codeUnit16 in str_1.utf16//这里返回的是一个utf8View类型的值，是一个包含多个Utf8的值的集合
    {
        print(codeUnit16,terminator:" ")//现在改成这个了 不换行输出用这个语句

    }



    ////这里一个表示4个字节 unicodeScalars返回的 UnicodeScalarView类型的值 包含一个或多个unicodeScalar的值
    ////是一个21位的Unitcode码，需要用.value属性获得数值是一个 Uint 32
    //for codeUnit32 in str_1.unicodeScalars//这里返回的是一个utf8View类型的值，是一个包含多个Utf8的值的集合
    //{
    //    print(codeUnit32.value,terminator:" ")//现在改成这个了 不换行输出用这个语句
    //
    //}
}

study++;

//------------------------- 运算符

if study < 100
{
    //%求余数运算符 ， 可以对小数求余数,两个操作数都是整数不能对0求余，有一个是浮点数，第二个可以是0.0或0结果是非数，余数的正负只和和除数的正负相关。

    //var mod = 7.5%1.2
    //
    //print(mod) // =0.3

    //溢出运算符，如果希望程序只是对溢出的数据位进行截断，而不是导致错误，可以使用Swift提供的溢出预算符

    //&(+ - * / %)

    //var a :UInt8 = 255
    //
    //var b:UInt8 = 255+a //这样竟然可以
    //
    //a = a &* 6 //溢出后进行截断操作

    //范围运算符 a...b [a b] a..<b [a b)

    for  var idx in 0...100 //(0..<100)
    {
        print(idx)
    }
    // 引用比较对象运算符 === 和 !==== 用来判断两个引用类型的变量是否指向同一个对象。，
    // == 用来比较内容

    var d1 = NSMutableArray();
    var d2 = NSMutableArray();
    //d1 == d2 但是 d1!===d2

    //nil 合并运算符  "a??b" 用来在可选类型的变量a是否为nil，如果为nil就返回后者b的值，a和b的类型必须一致

    //相当于 a!=nil?a!:b



}

study++ ;
if study < 100
{

    //var a=10
    //if a>0
    //{
    //    print(a)//即使这里只有一句 也必须用括号括起来
    //}
    //switch不用加break，每一个switch必须包含一条语句
    //不能这样
    /**
    * case 1:
    case 2:
    print();

    但是可以这样
    case 1,2:
    print();
    但是有的时候为了使用C语言里面的贯穿特性

    可以使用fallthrough
    */

    var num = 5;
    switch num
    {
    case 5...10: //可以表示范围
        print(num)
        //fallthrough 加上这句话下面的就会执行
    default:
        print(num+1)
    }
    var point:(x:Int,y:Int);
    point = (50,50)

    //（0，0）可以匹配到下面三个，遇到第一个就跳出，所以应该范围小的条件放在前面
    switch point
    {
//    case (x:0,y:0):  //(0,0) 也可以这样，
//        print("位于原点")
//    case (_,0)://元组里面可以匹配范围
//        print("x轴>")
//    case (0,_):
//        print("位于Y轴")
    case (0...100,0..<100):
        print("在第一象限")
        //default :
        //    break;

//    case (50,var y)://把元组的匹配的值放入变量y里面
//        print(y);
        //case let d :  //可以把switch的条件放到 变量或者常量中，如果希望在这里更改就用变量。这里let d 这语句可以匹配所有的情况所以可以替代default
    case var(x, y) where x > 0&&y>0: //在case里面可以进行再次筛选
        print("第一象限--》")

    default:
        break;
    }
    //case的值

    print(point)

    //循环结构

    //var cnt = 10
    //while cnt < 20
    //{
    //    print(cnt)
    //    cnt++
    //}
    //do
    //{
    //    cnt++
    //}while cnt<30

    for idx in 0 ..< 3 //for循环
    {
        print(idx)
    }
    for var a=0,b=0;a<0&&b<100;a++,b+=10//多个条件初始化
    {
        print(a,b)
    }
    //for ；； 死循环
    //{
    //
    //}

    //for in 用于遍历字符串，范围，序列和集合等包含的元素 这里的常量无需声明
    /**
    *  for 常量名|_ in 字符串|范围|集合
    */

    var friend = "myfriend"
    for /*char*/_ in friend.characters //friend.characters 是最新的，原来只要写friend就行
    {
        // print(char)
        print("kkk")//可以用 "_"来表示常量名，在不需要真正的遍历内容的时候可以用"_"
    }


    //这里的outer是一个标签，可以跳出到最外层的循环，有点类似于C语言的go语句，但是swift里面的标签只有放在循环
    //开头出才有用，就像下面这样
    outer:for i in 0  ..< 10
    {
        inner: for j in 0 ..< 10
        {
            print(i*j)
            if i*j==4
            {
                break outer
            }
            if j==7
            {
                continue//不会跳出循环
                //continue outer 这里也可以跟一个标签。忽略标签所指定的循环中剩下的语句，默认是inner
            }
        }
        //continue outer发生后后这里的代码不会执行
    }

}

// ----------------------------------- 4.集合


//var myArrs :Array<String>;//这里是声明,还不能用'
//
//
//var myArr2:[Int]


//这是构造器初始化
var myArr = Array<String>();//需要这样才能用
myArr.append("one")
myArr+["two"] //可以这样 写加入元素
var myArr2 = Array<String>(count: 10, repeatedValue: "ones")//可以这样


//这里是简化语法初始化

var b = ("books",15);

var values = ["a","b","c"]
//访问数组的元素可以使用下标
print(values[0])
values.count//数组的个数

for element in values
{
    print(element)
}
values .insert("d", atIndex: 0)
//数组的可变性和可修改性完全取决于是用let还是var
//数组的下标可以是范围
values[0...1]

//多维数组

var secArr :[[Int]] //一个Int类型的二维数组

secArr = Array<Array<Int>>(count: 4, repeatedValue: [])//表示最外层的数组装了四个一维数组

secArr[0] = Array<Int>(count: 5, repeatedValue: 1)



//----这样的循环过时了，不知道怎么写
for var i = 0,len = secArr.count;i<len;i += 1
{
    for var j = 0,len2 = secArr[i].count;j<len2;j += 1
    {
        print(secArr[i][j])//遍历每个数组内的数组元素

    }
}

//

var n = 4

var drawArr = Array<Array<Int>>(count: n, repeatedValue: []);//声明一个二维数组
for i in 0  ..< n 
{
    drawArr[0] = Array<Int>(count: n, repeatedValue: 0)
}

drawArr[0].append(10)
drawArr.removeLast();

var i = 0,j = 0, cur_pos = 1

var start = 0;


drawArr[0]

//-------字典 键和值用冒号隔开

var mydic:Dictionary=["key1":"value1","key2":"value2"]

//声明语法
var scores:Dictionary<String,String> //这里是逗号，

var scores2:[String:String] //这里是冒号：

//泛型创建字典

var scores3:Dictionary<String,Int>

scores3 = Dictionary(minimumCapacity: 5)

//updateValue会返回一个字典内value的可选类型的值（?)，如果原来的键值对不存在
//就返回nil
scores3.updateValue(100, forKey: "math")

scores3["chinese"]=90
scores3["english"]=80

for (item,score )in scores3// 前面是键后面对应的是值
{
    print(item,score)
    
}
var keys = Array(scores3.keys)//必须用Array构造器
scores3.count

//字典的删除
scores3.removeAll();
//集合的复制，包含的数据是值类型复制副本，引用类型复制指针

var arr_ori = ["one","two","three"]
var arr_cp1 = arr_ori;
var arr_cp2 = arr_ori

arr_cp1[0]+="1"
arr_cp2[0]+="2"

print(arr_cp1[0])
print(arr_cp2[0])

//引用复制

class User: NSObject {
    
    var name:String
    init(name:String) {
        self.name = name
    }
}


var UserOri =  [User(name: "wokong"),User(name: "bajie")]

var user1=UserOri;
var use2=UserOri
user1[0].name="oo"
use2[0].name="d"


---- 5--------------------- 闭包和函数

study+=1;


swift 不仅仅是面向对象的语言

/*
 
 func 函数名（形式参数列表）[->返回值类型]
 {
 
 }
 */
func max(x:Int,y:Int) -> Int {
    
    return x>y ? x:y
}
//支持多个返回值，需要元组的辅助

func getmessage(person:String) -> (String,String) {
    
    return ("test","100")
}

getmessage("mine")
//也可以指定元组的返回参数
func getmessage2(person:String) -> (name:String,height:String) {
    
    return ("test","100")
}
var msg = getmessage2("test")
print(msg.height,msg.name) //可以通过属性访问


//递归函数

func fn(i:Int) -> Int {
    
    if i==1 {
        return i
    }
    else
    {
        return i*fn(i-1);
    }
}

//fn(<#T##i: Int##Int#>) 这样只有数据类型不直观
print(fn(5))
//函数的形式参数 name和 msg都是参数

func sayHi(user name:String,msg:String) {
    
    print("程序正在执行sayHi（）函数")
    print("\(name),\(msg)")
}
//sayHi(user: <#T##String#>, msg: <#T##String#>);// 指定参数名

//用局部形参名做形式参数名 ----- 这里是swift修改了
//
//func sayHI2(#name:String) -> Void {
//    
//    
//}

//形参默认值,有默认值的话调用的时候可以省略形参,一般把带有默认值的放在后面。

func say2(user name:String="孙悟空") -> String {
    
    return name
}
print(say2());

//个数可变的形参，允许指定数量不确定的形参。在最有一个形参的类型后增加 ... 表示可以接受多个参数值,多个参数值被当成数组传入


func test(a :Int,books:String...) -> Void {
    
    for tmp in books {
        
        print(tmp)
    }
    
}

test(10, books: "OC","swift")

//常量形参和变量形参,形参函数默认的是常量，在函数内不能对参数列表的形参进行修改，如果需要直接使用形式参数做变量，而不需要在函数内重新定义，需要用var
//声明函数形参
//这里用var修饰已经提示过期了，就是参数就是不可修改的
func test2(/*var*/ width:Double,height:Double) -> Void {
    
//    width = 100;
    
    // 现在苹果不能用var修饰了
    
}

// in out形参,用inout关键字声明的形参可以进行传递真实的值，不是副本，类似于

//C语言的传地址，这里是可以修改的

func test3(inout a:Int,inout b:Int) -> Void {
    
    a = a-b
}
//print(test3(&<#T##a: Int##Int#>, b: &<#T##Int#>)) 打出来就是这个样子的

var s_a = 3,s_b=4;
test3(&s_a, b: &s_b)
print(s_a)


// 函数类型 --- C语言里面的函数指针

var p = test3; //可以直把函数赋值

var pType:(Int,Int)->Int

pType = max;

//函数类型做为参数
func fuc(a:Int,b:Int,p:(Int,Int)->Void) -> Void {
    
    p(a,b);
}
//函数重载，可以定义多个同名函数，但是要形式参数或者返回值不相同。

//函数的嵌套。默认的函数是全局的，嵌套在函数内的函数，在内部有效，可以通过

//前面的函数类型的返回值，返回具体的内容函数供外部调用

//----------------嵌套函数和闭包 ，闭包类似于Objective -C 里面的代码块

//-- 全局函数是一个有名称，但是不会捕获任何值的闭包。嵌套函数是一个有名字
//可以捕获封闭函数体的值的闭包，闭包表达式是一个简洁的，可以捕获封闭函数体的值的匿名闭包

//定义闭包

func getMathFunc(type:String) ->(Int)->Int  {
    
    
    switch type {
    case "square":
        return{ (val:Int) ->Int in //闭包 ,放在一个大括号内，没有func关键字和函数名字
             return val*val
        }
    case "cube":
        return{ (val:Int) -> Int in
            return val*val*val;
        }
    default:
        return {
            (val:Int) -> Int in
            
            var t = 0;
            for  i in 0...100
            {
                t+=i;
            }
            return val*t;
        }
    }
    
}
var mathFunc = getMathFunc("cube");

print(mathFunc(5));

//省略形参名。闭包表达式可省略，返回值的情况下，关键字 in也可以省略，闭包表达式可以通过$0,$1，$2来引用第一个，第二个。。。形参

//尾随闭包

//函数的参数类型是函数类型和闭包类型是相同的，可以传递闭包也可以传递函数

func someFunction (val:Int,fn:(Int)->(Int))->(Int)
{
    //执行代码
   return fn(val)
}

var sqare = { (val:Int)->Int in
    return val*val;//闭包只有一行代码，return 关键字
}
print(someFunction(10, fn: sqare))

//这是普通闭包

someFunction(20, fn: {//放的是执行体
    (val:Int)->Int in
    return val*val;//闭包只有一行代码，return 关键字

})
//这里是尾随闭包
someFunction(20)//闭包表达式在花括号的外面
{
    (val:Int)->Int in
    return val*val;//闭包只有一行代码，return 关键字
}

//闭包捕获上下文的变量和常量，可以修改上下文中的常量和变量，即使定义这些变量和常量的作用域不存在了，也可以访问和修改？？？？

func makeArray(ele:String)->()->[String] //返回的是一个没有参数，返回值为String的函数
{
    var arr:[String] = []

    
    func addElement()->[String]
    {
        arr.append(ele)
        return arr
    }
    return addElement
}

//这注意
let add1 = makeArray("one")
print(add1())
print(add1())
let add2 = makeArray("two")
print(add2())//add2h额add1没有关系

//闭包是引用类型

var add3 = add2 //add3 直接更改值
print(add3())


//------ 面向对象上，除了定义自定义类还有枚举，结构，类。都支持定义存储属性，计算属性，方法，下标，高早起和嵌套类型

//------上面这句话暂时理解不了，信息量有点大呀。。。  说支持所有的类型。类，结构体，枚举这三种程序单元的内容分别可以定义属性（存储属性和计算属性），方法，下标，构造器和嵌套类型（嵌套类，嵌套结构体，嵌套枚举）
//5种类型的成员，这5种成员在类，结构体，枚举各有区别



//-- 定义枚举

enum Season
{
    case Spring // case 关键字
    case Summer
    case Fall
    case Winter
    
    //case one,two 这样也是可以的
}// 这里没有给分配一个默认的整数值。枚举名就代表了一个实例。和整数没有任何关系

//定义了swift任意类型后，以下几方面使用

/*

1.声明变量
2.创建实例
3.访问类型属性
4.调用类型方法
5.派生子类

*/
//声明变量
var sn :Season;
sn = Season.Spring//可以直接用枚举的实例赋值

switch (sn)
{
    case .Spring:
        print("spring")
    case .Summer:
        print("summer")
    default :
    print("default")
}

enum enum2:Int
{
    case one,two=3,three //one = 0.three = 4
}
enum enum3:Character {
    case one = "O"
}
//要么不指定，要么全部都必须确定有值，指定属性可以使用构造器的属性
var day = enum2.init(rawValue: 3);//根据值获取枚举值,可能失败的构造器

var day2 = day?.rawValue
//枚举的关联值
//case 枚举值 （元组语法)

enum Planet
{

    case Earth(Double,String)
    case Mars(density:Double,name:String)
    case Sun
}

var p1 = Planet.Earth(1.0, "地球")
var p2 = Planet.Mars(density: 2.0, name: "火星")

switch(p2)
{
    case  Planet.Earth(var weight,var name): //真是奇葩，可以这样关联
        print(weight,name)
    default:
        break;
}
//类和结构体 
/*
 区别：
 1结构体不支持继承
 2.不支持自定义构造器
 3.结构体是指类型，不是引用类型
 */

//swift的属性分为 存储属性（实例变量)和计算属性(相当于property合成的属性)
//class表示一个属性或者方法是否属于类，static 用于结构体和枚举相当于类里面的class

class person:  NSObject {
    
    var name:String=""//必须初始化。
    var age:Int=0 ;
    func say() -> Void {
        
        print("\(name) -- \(age)")
    }
    func say(message:String) -> Void {//函数                                                                                                                                                                                                                                                                        持重载
        
        print("\(message)")
    }
    func  growp(a:Int) -> person {
        
        age+=a
        return self
    }
//    init(n:String,a:Int) {//显示 定义带参数的构造器，没有自定义系统会提供一个默认的
//        
//        self.age=a;
//        self.name=n
//    }
}

var per1 = person();
per1.name = "P1"
per1.age = 0;
per1.say()
per1.say("消息")

var per2 = person()
per2.name="P1"
per2.age=0;


//===判断应用类型，==比较值类型,可以重载==号，自己定义比较类型

func ==(p1:person,p2:person) -> Bool  {
    
    if p1.name==p2.name&&p1.age==p2.age {
        
        return true
    }
    return false
}

print(per2==per1)

per1.growp(1).growp(2) //这里类似于OC里面的链式编程


//存储属性-- 存储在类，结构体，枚举内部的常量或者变量。
struct FixedLengthRange {
    
    var start:Int
    //定义常量存储属性
    let length :Int
    //系统会为 "结构体"   提供所有存储属性的构造器，所以可以不指定初始值
}

var rg = FixedLengthRange(start: 2, length: 3);
//rg.length=0 初始化后就不能改

class lazyC: NSObject {
    
    lazy var tem = 100//懒加载关键在，类似于OC里面的重写get方法里面初始化，通过.语法访问才初始化
}
//计算属性 property属性，实例计算属性和类计算属性

enum XBSeanon :Int{
    case Spring,Summer,Fall,Winter
    //info就是计算属性
    var info:String{
        //在这里写get和set方法
        get{
            switch self {   //去掉get方法就是只读属性
            case .Spring:
                return "春"
            default:
                return "非春天"
            }
        }
        set(newValue)//这个形参可以省略，
        {
            if newValue=="春天" {
                self = .Spring
            }
            else
            {
                self = .Summer//瞎写测试
            }
        }
    
    }
    mutating func changeV() //mutatiing 表示可变，用在结构体和枚举
    {
        self = XBSeanon(rawValue: (self.rawValue+1)%3)!
    }
}

//var mys = XBSeanon.Spring
//print("\(mys.info)")//get
//mys.info = "春天"
////属性观察者。类似于KVO或者重写set方法实现的功能

class User {
    //这里的属性观察没有什么意义，因为没有重写setter方法和getter方法，对属性赋值操作检查应该在set方法做检查，
    var name :String = "" {//不能监听懒加载的熟悉
        
        willSet{
            //这里放的是属性赋值前，自动要执行的代码
            if newValue.characters.count<3 {
                print("小于3")
            }
        }
        didSet
        {
            print("已经设置成功了")
        }
        /*
        get
        {
        return "success"
        }
        set {
        self.name = "fail"  set和get代码和上面的不能共存，set个get要求没初始化，willSet和didSet应
        }
        }*/

    }
    func move(x:Int)
    {
        
    }
}
var U = User()
U.name="1"
U.name

//属性和方法统一
//可以直接函数类型定义存储属性，并将函数和闭包传递就行

//下标

struct FKRect {
    
    var x:Int
    var y:Int
    var width:Int
    var heigth:Int
    subscript (idx:Int) ->Int  //输入参数不支持指定外部参数，返回值可以返回任意类型，数组就是下标，字典就是value的类型
    {
        get{
            //定义一段可执行的代码，
            switch(idx)
            {
                case 0:
                    return self.x
                case 1:
                    return self.y
                case 2:
                    return self.width;
                case _ :
                    return self.heigth
                
            }
        }
        set
        {
            switch (idx)
            {
                case 0:
                    self.width = newValue
                default:
                    self.heigth = newValue
            }
        }
    }
    //int 是访问的形式，可以是字符串，联想字典 dict[@"one"];
    subscript (idx:Int) -> String  //输入参数不支持指定外部参数，返回值可以返回任意类型，数组就是下标，字典就是value的类型
        {
        get{
            //定义一段可执行的代码，
            switch(idx)
            {
            case 0:
                return "width"
            case 1:
                return "height"
            case 2:
                return "x"
            case _ :
                return "y"
            }
        }
        set
        {
            switch (idx)
            {
            case 0:
                self.width = 12
            default:
                self.heigth = 123
            }
        }
    }

    
}

var rect = FKRect(x: 0, y: 0, width: 100, heigth: 100)
rect[0]=100 //通过下标引用

var num:String = rect[0]
print(rect.width)
//--------------------------可选择链
//可选链代替强制解析

class BOOK {
    
    var bookName:String!
    init(name:String)
    {
        self.bookName = name
    }
}
class Student {
    
    var book:BOOK!
    
}
class School {
    
    var stu:Student!
}
var school = School()
//print(school.stu.book)// 这样写就错了。可以把！换成？作为可选链。！表示隐式解析，？表示可选链。中间有一个为nil就会返回nil，不会继续向后执行。
print(school.stu?.book)
//可选链 适用于 属性，方法，下标

//-----类属性和类方法 （枚举，类，结构体自身的方法)

//--值的类型属性包含枚举和结构体，用static修饰，包含存储属性和计算属性。“”“”类的类型属性只能定义计算属性“”“”“”“
//构造器---无须显示的声明返回值类型，无须显示的使用return返回实例，名字都是init函数，可以有多个函数重载
/**
 *  对于结构体提供两个构造器，无参数的和初始化所有的。对于类提供了无参数的。需要自定义的时候就自定义，自定义后就不在提供默认的
 
 
 构造器的外部参数和局部参数相同--swift做的。
 */

class TestInit {
    
    var name:String?
    let const = 10
//    
//    init(_ n:String)
//    {
//        self.name = n
//        //self.const = 100//这里是不能被修改的。据课本上说原来是可以的
//    }
    //构造器也可以重载
    //可能失败的构造器，
    init?(_ n:String)//可能失败的构造器
    {
        if n.characters.count<1 {
            return nil
        }
    }
}
//var testI = TestInit.init(n: "a")

var testII = TestInit.init("A")//隐藏外部参数名字
//可能失败的构造器传播：可能失败的构造器可以调用同一个类中的普通构造器，普通构造器不能调用同一个类中可能失败的构造器，但是在结构体中普通的可以调用可能失败的。

class M1 :TestInit{
    
    var grade:Int!
    init!(name:String,grade:Int)
    {
        super.init(name)//可能失败的调用 正常的
    }
   }
class M2 :M1{
    
    var grade2:Int!
//    override init(name:String,grade:Int) //普通的不能调用可能失败的。
//    {
//      //  super.init?(name: name, grade: grade)//可能失败的调用 正常的
//    }
}

//------------------------------------- 面向对象下



//********重写父类的属性，下标，观察者貌似全部更改了********/

//子类重写父类的属性

class brid {
    
    static var speed:Double = 0
}

// ----- 变了----
//var eagle:brid
//{
//   var speed:Double{//重写父类的属性   //现在改了，不能这样写了。只能
//        
//        get{
//            print("正在访问被重写的属性")
//        }
//        set{
//            super.speed = 100
//        }
//    }
//}


//var eagle:brid
//{
//   var speed:Double{//重写父类的属性   //现在改了，不能这样写了。只能
//        get{
//            print("正在访问被重写的属性")
//        }
//        set{
//            super.speed = 100
//        }
//    }
//}

//--final 可以修饰类，属性，方法和下标,防止被集成，重写，
final class Base
{
    final var name :String=""
    final func say (content:String)
    {
        print("Base对象说\(content)")
    }
}
//类的构造和析构

class Apple {
    
    var name:String!
    var weight:Double
    
    init(name:String,weight:Double)//制定构造器，1个或者多个，完成所有的属性初始化，可以调用父类的制定构造器，如果有父类，必须调用父类的init
    {
        self.name = name;
        self.weight = weight
    }
    convenience init(n:String)//便利构造器
    {
        self.init(name:n,weight: 100)//必须调用其他构造器 ，或者最终调用的是指定构造器
        self.name = "xiugaile "
    }
}

//多态
class baseClass
{
    func test()
    {
        print("基类的方法")
    }
}

class subClass:baseClass
{
    override func test() {
        print("这是子类")
    }
}
class subClass2:baseClass
{
    override func test() {
        print("这是子类2")
    }
    enum type
    {
       case height,low
    }
}

var instant:baseClass = subClass();

instant.test();//这里执行的其实是子类
//is运算符检验类型 相当于 isKindOf isMemberOf
print("\(instant is subClass)")
//as运算符向下转型
var instant2 = instant as? subClass2//类型强制转换,可能失败
//两个不确定的类型特殊类型
var arr:[Any] = ["one",1] //any表示任意类型，anyobject表示任意对象


// -- 扩展

extension String
{
    //扩展方法
    func XBLength(Str:String) ->Int
    {
        return Str.characters.count;
    }
}

// -- 协议

@objc protocol Draw //可选类型要有objc符号
{
    @objc optional
    var drawCirle:Double{get set};//属性
    func drawCubic(width:Double);//方法
}

class SwitftDraw:Draw
{
    
    @objc var drawCirle:Double
        {
        get{
            return self.drawCirle;
        }
        set
        {
            self.drawCirle = newValue;
        }
    }
    @objc func drawCubic(width: Double) {
        
        print("立方体 \(width)")
    }
}
//泛型 类型占位符

func copyArray <T> (src:[T],inout dest:[T])
{
    
    for element in src
    {
        dest.append(element)
    }
    
}
//这个方法可以复制任意类型的数组
//可以定义多个不同的参数
func projection<SrcType,DesType>(src :[SrcType],fn:(SrcType)->DesType) ->[DesType]

{
    var result = [DesType]();
    for element in src
    {
        result.append(fn(element));
    }
    return result;
}

var books = ["C和指针","算法导论"]

var proj = projection(books){
    
    $0.characters.count
}
//类也可以使用泛型
class AppleS<T:Comparable>//这里通过协议对泛型进行限定
{
    var info:T
    init(info:T)
    {
        self.info = info
    }
}
extension AppleS
{
    //扩展类可以直接使用泛型
    func bigThan(other:AppleS,fn:(T,T) ->Int) ->Bool {
        
        if fn(self.info,other.info)>0
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}





















